package com.hup.utils.commons.start;

import com.hup.utils.commons.exception.AppExceptionHandler;
import com.hup.utils.commons.exception.ErrInfo;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

/**
 * 系统状态栏托盘图标;允许省缺情况下,没有菜单,没有鼠标回调
 * 一个程序可以有多个图标;
 * <pre>
 * 解决问题:
 *  原生PopupMenu问题:不支持中文,图标等缺陷;
 *  使用JPopupMenu后,菜单展示后,点击其他地方不会自动隐藏等问题;
 * 资料:
 *  https://blog.csdn.net/TianYaZhongBieLi/article/details/102903765
 * </pre>
 *
 * @author hugan
 * @date 2020/4/2
 */
public class BaseAppTray extends TrayIcon {

    /**
     * nullable
     */
    private SimpleMenu menu;
    /**
     * nullable
     */
    private TrayActionListener trayListener;

    /**
     * @param imgPath 仅jar内部图像,如:[/img/cursor.gif]
     * @param tooltip 一般是appName;多个图标时,根据需要设定
     */
    public BaseAppTray(String imgPath, String tooltip) {
        this(AppIcon.getIcon(imgPath, "托盘图标").getImage(), tooltip);
    }

    /**
     * @param tooltip 一般是appName;多个图标时,根据需要设定
     */
    public BaseAppTray(Image image, String tooltip) {
        super(image);
        setImageAutoSize(true);
        setToolTip(tooltip);

        addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                if (SwingUtilities.isRightMouseButton(e)) {
                    if (menu != null) {
                        if (trayListener != null) trayListener.beforeShowMenu();
                        //直接使用show方法,会导致,鼠标在item上移动时没有聚焦效果
                        //menu.show(e.getComponent(), e.getX(), e.getY());
                        menu.setLocation(e.getX(), e.getY());
                        menu.setInvoker(menu);
                        menu.setVisible(true);
                    }
                } else if (SwingUtilities.isLeftMouseButton(e)) {
                    if (trayListener != null) trayListener.onClickTray();
                }
            }
        });
    }

    public void doShow() {
        try {
            SystemTray.getSystemTray().add(this);
        } catch (Exception e) {
            ErrInfo errInfo = AppExceptionHandler.handle("图标展示失败,", e);
            DialogHelper.confirm(errInfo.getErrMsg()).show();
        }
    }

    public void doHide() {
        SystemTray.getSystemTray().remove(this);
    }

    public void setTrayListener(TrayActionListener trayListener) {
        this.trayListener = trayListener;
    }

    /**
     * @deprecated 不支持中文, 使用 {@link #setPopupMenu(SimpleMenu, ActionListener)}
     */
    @Override
    @Deprecated
    public void setPopupMenu(PopupMenu popup) {
        super.setPopupMenu(popup);
    }

    public SimpleMenu createMenu() {
        SimpleMenu res = new SimpleMenu();
        res.dialog.setIconImage(getImage());
        return res;
    }

    /**
     * @param menu nullable, {@link #createMenu()}
     * @param listener nullable,自定义菜单项的监听(不包括[退出]菜单的)
     * @apiNote 如果要实现[展示菜单前刷新菜单]的功能,则调用 {@link #setTrayListener(TrayActionListener)}
     */
    public void setPopupMenu(SimpleMenu menu, ActionListener listener) {
        this.menu = menu;
        if (menu == null || listener == null) return;
        for (MenuElement item : menu.getSubElements()) {
            if (item != menu.menuExit) {
                ((JMenuItem) item.getComponent()).addActionListener(listener);
            }
        }
    }


    /**
     * 使用隐藏的dialog实现失去焦点后隐藏托盘菜单
     */
    private static class AutoHideMenu extends JPopupMenu implements FocusListener {
        public JDialog dialog;

        private AutoHideMenu() {
            dialog = new JDialog((Frame) null, "图标菜单");
            dialog.setUndecorated(true);
            dialog.setSize(1, 1);
            dialog.setLocation(-10, -10);
            dialog.addFocusListener(this);
        }

        @Override
        public void setVisible(boolean b) {
            //处理展示菜单时,同时展示dialog
            dialog.setVisible(b);
            super.setVisible(b);
            if (b) {
                dialog.requestFocus();
            }
        }

        @Override
        public void focusLost(FocusEvent e) {
            //dialog失去焦点时的操作:隐藏菜单
            setVisible(false);
        }

        @Override
        public void focusGained(FocusEvent e) {
            //do nothing
        }
    }

    public class SimpleMenu extends AutoHideMenu {
        private static final String CMD_EXIT = "退出";
        private JMenuItem menuExit;

        /**
         * 添加居中的菜单头部
         */
        public SimpleMenu addHeader(String desc) {
            addHeader(SwingConstants.CENTER, desc);
            return this;
        }

        /**
         * 添加菜单头部
         *
         * @param horizontalAlignment {@link SwingConstants#LEFT}, {@link SwingConstants#CENTER}, {@link SwingConstants#RIGHT}
         */
        public SimpleMenu addHeader(int horizontalAlignment, String desc) {
            //必须是html和JLabel.horizontalAlignment一齐才能达到效果
            switch (horizontalAlignment) {
                case SwingConstants.CENTER:
                    desc = "<html><p align='center'>" + desc + "</p><html>";
                    break;
                case SwingConstants.RIGHT:
                    desc = "<html><p align='right'>" + desc + "</p><html>";
                    break;
            }
            add(new JLabel(desc, horizontalAlignment));
            return this;
        }

        /**
         * 添加默认的退出功能菜单;
         * 如果没有调用添加监听 {@link BaseAppTray#setTrayListener(TrayActionListener)},则操作是[隐藏图标,并退出程序]
         */
        public SimpleMenu addExit() {
            menuExit = add(CMD_EXIT);
            menuExit.addActionListener(e -> {
                if (trayListener != null) {
                    trayListener.onExit(BaseAppTray.this);
                } else {
                    BaseAppTray.this.doHide();
                    System.exit(0);
                }
            });
            return this;
        }

        /**
         * @param comp {@link JMenuItem}, {@link JCheckBoxMenuItem}, {@link JRadioButtonMenuItem}
         */
        public SimpleMenu addMenu(JMenuItem comp) {
            super.add(comp);
            return this;
        }

        public SimpleMenu addMenu(String text) {
            super.add(text);
            return this;
        }

        public SimpleMenu addSeparator0() {
            super.addSeparator();
            return this;
        }

    }

    /**
     * 托盘图标功能回调
     */
    public interface TrayActionListener {
        default void beforeShowMenu() {}

        /**
         * 鼠标左键点击图标
         */
        default void onClickTray() {}

        /**
         * @param appTray 用于卸载托盘图标, {@link BaseAppTray#doHide()}
         */
        default void onExit(BaseAppTray appTray) {}
    }

}
